﻿using System;

using System.IO;
using System.IO.Compression;
using System.Text;
using System.Text.RegularExpressions;
using System.Collections.Generic;
using System.Collections.ObjectModel;

using System.Web;
using System.Net;
using System.Threading;
using System.Diagnostics;

namespace LZRobot
{

    ///////////////////////////////////////////////////////////////////////////////////////////////////////////
    //分析给定的文本，解析字符串等相关操作
    public class TextParser
    {
        private string content = null;
        private string startSign = null;
        private string endSign = null;
        private int curPos = 0;

        public TextParser(string con)
        {
            this.content = con;
        }

        public string getStrBtw( string start, string end )
        {
            this.curPos = 0;
            this.startSign = start;
            this.endSign = end;

            return this.getNextStrBtw();
        }

        public string getNextStrBtw()
        {
            if (string.IsNullOrEmpty(this.content)) return null;

            //Get string end pos
            int strBeginPos = 0;
            if (string.IsNullOrEmpty(this.startSign))
            {
                strBeginPos = this.curPos;
            }
            else
            {
                strBeginPos = this.content.IndexOf(this.startSign, this.curPos, StringComparison.CurrentCultureIgnoreCase);
                if (-1 == strBeginPos) return null;
            }
            strBeginPos += this.startSign.Length;

            //Get string end pos
            int strEndPos = 0;
            if (string.IsNullOrEmpty(this.endSign))
            {
                strEndPos = this.content.Length;
            }
            else
            {
                strEndPos = this.content.IndexOf(this.endSign, strBeginPos, StringComparison.CurrentCultureIgnoreCase);
                if (-1 == strEndPos) return null;
            }
            this.curPos = strEndPos + this.endSign.Length;

            //Return trimed string
            return this.content.Substring(strBeginPos, strEndPos - strBeginPos).Trim();
        }

        //1天3分
        //15小时35分
        //18小时整
        //35分
        public int getTotalMinutes()
        {
            string day = this.getStrBtw("", "天");
            if (null == day) day = "0";

            string hour = this.getStrBtw("天", "小时");
            if (null == hour)
            {
                hour = this.getStrBtw("", "小时");
            }
            if (null == hour) hour = "0";


            if (null == hour) hour = "0";
            string min = this.getStrBtw("小时", "分");
            if (null == min)
            {
                min = this.getStrBtw("天", "分");
            }
            if (null == min)
            {
                min = this.getStrBtw("", "分");
            }
            if (null == min) min = "0";


            int ret = GetInteger(day) * 24 * 60 + GetInteger(hour) * 60 + GetInteger(min);
            return ret;
        }

        public bool contains( string str )
        {
            return this.content.IndexOf(str) > -1;
        }

        public static int GetInteger(string str)
        {
            if (string.IsNullOrEmpty(str))
            {
                return -1;
            }
            int id;
            if (int.TryParse(str, out id))
            {
                return id;
            }
            return -1;
        }

        public static List<int> GetIntegerList(string str)
        {
            string del = ",";
            List<int> result = new List<int>();
            if (string.IsNullOrEmpty(str))
            {
                return result;
            }
            string[] parts = str.Split(del.ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
            foreach (string part in parts)
            {
                int id = GetInteger(part);
                if (id != -1)
                {
                    result.Add(id);
                }
            }
            return result;
        }

        public static string NoHtml(string result)
        {
            if (result == null)
            {
                return string.Empty;
            }
            StringBuilder builder = new StringBuilder();
            bool flag = false;
            foreach (char ch in result)
            {
                switch (ch)
                {
                    case '<':
                        flag = true;
                        break;

                    case '>':
                        flag = false;
                        break;

                    default:
                        if (!flag)
                        {
                            if (ch == '\n')
                            {
                                builder.Append(" ");
                            }
                            else if (((ch != ' ') && (ch != '\t')) && (ch != '\r'))
                            {
                                builder.Append(ch);
                            }
                        }
                        break;
                }
            }
            return builder.ToString().Trim().Replace("&yen;", "￥").Replace("&nbsp;", "");
        }

    }



    ///////////////////////////////////////////////////////////////////////////////////////////////////////////
    public class HttpHelper
    {
        // 无 referer 的 POST  // by hook.hu@gmail.com
        public string Post(string url, string content)
        {
            string data = Post(url, content, _lastUrl);
            _lastUrl = url;
            return data;
        }

        //Simulate IE properties
        protected void setReqProps(HttpWebRequest req)
        {
            req.Accept = "*/*";
            req.Headers.Set("Accept-Language", "zh-cn");
            req.Headers.Set("UA-CPU", "x86");
            req.Headers.Set("Accept-Encoding", "gzip, deflate");
            req.UserAgent = "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.2; .NET CLR 1.1.4322; InfoPath.2; .NET CLR 2.0.50727; .NET CLR 3.0.04506.648; .NET CLR 3.5.21022; MAXTHON 2.0)";
        }

        //Get response with different format
        protected string getResp(HttpWebRequest req)
        {
            //接收返回字串
            HttpWebResponse res = (HttpWebResponse)req.GetResponse();
            //Decode by content type
            string encoding = res.Headers["Content-Encoding"];
            StreamReader sr = null;
            if (encoding.Contains("gzip"))
            {
                sr = new StreamReader(new GZipStream(res.GetResponseStream(), CompressionMode.Decompress), Encoding.UTF8);
            }
            else if (encoding.Contains("deflate"))
            {
                sr = new StreamReader(new DeflateStream(res.GetResponseStream(), CompressionMode.Decompress), Encoding.UTF8);
            }
            else
            {
                sr = new StreamReader(res.GetResponseStream(), Encoding.UTF8);
            }
            return sr.ReadToEnd();
        }

        /// <summary>
        /// Send post data to server
        /// </summary>
        /// <param name="url"></param>
        /// <param name="content"></param>
        /// <returns>Return content</returns>
        public string Post(string url, string content, string referer)
        {
            int failedTimes = _tryTimes;
            while (failedTimes-- > 0)
            {
                try
                {
                    DelaySomeTime();

                    HttpWebRequest req = (HttpWebRequest)WebRequest.Create(new Uri(url));
                    req.UserAgent = reqUserAgent;
                    req.CookieContainer = _cc;
                    req.Referer = referer;
                    byte[] buff = Encoding.GetEncoding("GB2312").GetBytes(content);
                    req.Method = "POST";
                    req.Timeout = _timeout;
                    req.ContentLength = buff.Length;
                    if (null != _proxy && null != _proxy.Credentials)
                    {
                        req.UseDefaultCredentials = true;
                    }
                    req.Proxy = _proxy;

                    req.ContentType = "application/x-www-form-urlencoded";
                    req.Headers.Set("Pragma", "no-cache");
                    this.setReqProps(req);

                    //req.Connection = "Keep-Alive";
                    Stream reqStream = req.GetRequestStream();
                    reqStream.Write(buff, 0, buff.Length);
                    reqStream.Close();

                    return this.getResp(req);
                }
                catch (Exception e)
                {
                    TraceLog.Error("HTTP POST Error: " + e.Message);
                    TraceLog.Error("Url: " + url);
                    TraceLog.Error("Data: " + content);
                }
            }

            return string.Empty;
        }

        protected void DelaySomeTime()
        {
            if (_delayTime > 0)
            {
                Random rd = new Random();
                int delayTime = _delayTime * 1000 + rd.Next(1000);
                Thread.Sleep(delayTime);
            }
        }

        // 无 referer 的 POST  // by hook.hu@gmail.com
        public string Get(string url)
        {
            string data = Get(url, _lastUrl);
            _lastUrl = url;
            return data;
        }

        /// <summary>
        /// Get data from server
        /// </summary>
        /// <param name="url"></param>
        /// <returns>Return content</returns>
        public string Get(string url, string referer)
        {
            int failedTimes = _tryTimes;
            while (failedTimes-- > 0)
            {
                try
                {
                    DelaySomeTime();

                    HttpWebRequest req = (HttpWebRequest)WebRequest.Create(new Uri(url));
                    req.UserAgent = reqUserAgent;
                    req.CookieContainer = _cc;
                    req.Referer = referer;

                    req.Method = "GET";
                    req.Timeout = _timeout;
                    if (null != _proxy && null != _proxy.Credentials)
                    {
                        req.UseDefaultCredentials = true;
                    }
                    req.Proxy = _proxy;
                    //req.Connection = "Keep-Alive";
                    this.setReqProps(req);

                    //接收返回字串
                    HttpWebResponse res = (HttpWebResponse)req.GetResponse();

                    return this.getResp(req);
                }
                catch (Exception e)
                {
                    TraceLog.Error("HTTP GET Error: " + e.Message);
                    TraceLog.Error("Url: " + url);
                }
            }

            return string.Empty;
        }

        /// <summary>
        /// Set Proxy
        /// </summary>
        /// <param name="server"></param>
        /// <param name="port"></param>
        public void SetProxy(string server, int port, string username, string password)
        {
            if (null != server && port > 0)
            {
                _proxy = new WebProxy(server, port);
                if (null != username && null != password)
                {
                    _proxy.Credentials = new NetworkCredential(username, password);
                    _proxy.BypassProxyOnLocal = true;
                }
            }
        }

        /// <summary>
        /// Set delay connect time
        /// </summary>
        /// <param name="delayTime"></param>
        public void SetDelayConnect(int delayTime)
        {
            _delayTime = delayTime;
        }

        /// <summary>
        /// Set the timeout for each http request
        /// </summary>
        /// <param name="timeout"></param>
        public void SetTimeOut(int timeout)
        {
            if (timeout > 0)
            {
                _timeout = timeout;
            }
        }

        /// <summary>
        /// Set the try times for each http request
        /// </summary>
        /// <param name="timeout"></param>
        public void SetTryTimes(int times)
        {
            if (times > 0)
            {
                _tryTimes = times;
            }
        }

        /// <summary>
        /// Encode post data
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public static string EncodePostData(string data)
        {
            return HttpUtility.UrlEncode(data);
        }

        public void Download(string url, string localfile)
        {
            WebClient client = new WebClient();
            client.DownloadFile(url, localfile);
        }

        public void SetConfig(GlobalConfigData globalConfigData)
        {
            if (null != globalConfigData)
            {
                SetProxy(globalConfigData.ProxyServer, globalConfigData.ProxyPort, globalConfigData.ProxyUser, globalConfigData.ProxyPass);
                SetDelayConnect(globalConfigData.ConnectDelayTime);
                SetTimeOut(globalConfigData.HttpTimeout);
                SetTryTimes(globalConfigData.HttpTryTimes);
            }
        }

        public HttpHelper()
        {
            //_cc.Add(new Cookie("_user", "5ea81b6fe01e99584f291463ceb70bc5_12832911_1232504287", "/", ".kaixin001.com"));
            //_cc.Add(new Cookie("_invisible", "0", "/", ".kaixin001.com"));
            //_cc.Add(new Cookie("_uid", "12832911", "/", ".kaixin001.com"));
            //_cc.Add(new Cookie("_email", "shihan.han%40139.com", "/", ".kaixin001.com"));
            //_cc.Add(new Cookie("_kx", "2387b2d027e15bd6efce49aa1d77205a_12832911", "/", ".kaixin001.com"));
            //_cc.Add(new Cookie("SERVERID", "_srv102-109_", "/", ".kaixin001.com"));
        }

        #region Member Fields
        protected CookieContainer _cc = new CookieContainer();
        protected WebProxy _proxy;

        protected int _delayTime;
        protected int _timeout = 120000; // The default is 120000 milliseconds (120 seconds).
        protected int _tryTimes = 3; // 默认重试3次
        protected string _lastUrl = string.Empty;
        protected string reqUserAgent = "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; SLCC1; .NET CLR 2.0.50727; .NET CLR 3.0.04506; InfoPath.2)";
        #endregion
    }



    ///////////////////////////////////////////////////////////////////////////////////////////////////////////
    public class GlobalConfigData
    {
        public string ProxyServer; // 代理
        public int ProxyPort; // 代理
        public string ProxyUser; // 代理
        public string ProxyPass; // 代理
        public int ConnectDelayTime = 5; // 链接延时时间
        public int TryTimes = 3; //如果停车失败，重新尝试的次数
        public int RepeatTime = 0; // 循环停车时间(分钟), 0表示只执行一次
        public int HttpTimeout = 120000; // 设置每次链接的超时(秒)，默认是120秒
        public int HttpTryTimes = 3; // 设置每次HTTP请求失败后尝试的次数，默认是3次
        public bool WriteToLogFile; // 是否记录到log.txt文件
    }



    ///////////////////////////////////////////////////////////////////////////////////////////////////////////
    public static class GlobalConfigHelper
    {
        public static GlobalConfigData ReadGlobalConfigData(string filename)
        {
            GlobalConfigData config = new GlobalConfigData();
            try
            {
                string[] lines = File.ReadAllLines(filename);
                foreach (string line in lines)
                {
                    if (line.StartsWith("proxyserver=", StringComparison.OrdinalIgnoreCase))
                    {
                        config.ProxyServer = GetStringValue(line);
                        continue;
                    }

                    if (line.StartsWith("proxyport=", StringComparison.OrdinalIgnoreCase))
                    {
                        config.ProxyPort = GetIntegerValue(line);
                        continue;
                    }

                    if (line.StartsWith("proxyuser=", StringComparison.OrdinalIgnoreCase))
                    {
                        config.ProxyUser = GetStringValue(line);
                        continue;
                    }

                    if (line.StartsWith("proxypass=", StringComparison.OrdinalIgnoreCase))
                    {
                        config.ProxyPass = GetStringValue(line);
                        continue;
                    }

                    if (line.StartsWith("DelayConnect=", StringComparison.OrdinalIgnoreCase))
                    {
                        config.ConnectDelayTime = GetIntegerValue(line);
                        continue;
                    }

                    if (line.StartsWith("RepeatTime=", StringComparison.OrdinalIgnoreCase))
                    {
                        config.RepeatTime = GetIntegerValue(line);
                        continue;
                    }

                    if (line.StartsWith("TryTimes=", StringComparison.OrdinalIgnoreCase))
                    {
                        config.TryTimes = GetIntegerValue(line);
                        continue;
                    }

                    if (line.StartsWith("HttpTimeout=", StringComparison.OrdinalIgnoreCase))
                    {
                        config.HttpTimeout = GetIntegerValue(line) * 1000;
                        continue;
                    }

                    if (line.StartsWith("HttpTryTimes=", StringComparison.OrdinalIgnoreCase))
                    {
                        config.HttpTryTimes = GetIntegerValue(line);
                        continue;
                    }

                    if (line.StartsWith("EnableLog=", StringComparison.OrdinalIgnoreCase))
                    {
                        config.WriteToLogFile = GetBoolValue(line);
                        continue;
                    }
                }
            }
            catch (Exception e)
            {
                TraceLog.Error(e.Message);
                return null;
            }

            return config;
        }

        public static void GetIntegerListValue(Collection<int> list, string line)
        {
            string[] idlist = GetStringValue(line).Split(',');
            foreach (string id in idlist)
            {
                int value = TextParser.GetInteger(id);
                if (-1 != value)
                {
                    list.Add(value);
                }
            }
        }

        public static bool GetBoolValue(string line)
        {
            return ("1" == GetStringValue(line));
        }

        public static int GetIntegerValue(string line)
        {
            string value = GetStringValue(line);

            return TextParser.GetInteger(value);
        }

        public static string GetStringValue(string line)
        {
            return line.Substring(line.IndexOf('=') + 1);
        }
    }



    ///////////////////////////////////////////////////////////////////////////////////////////////////////////
    public static class TraceLog
    {
        public static void Print(string format, params object[] args)
        {
            string msg = GetMessage(format, args);
            DateTime dt = System.DateTime.Now.ToLocalTime();

            ConsoleColor color = Console.ForegroundColor;
            Console.ForegroundColor = ConsoleColor.Gray;
            if (_newLine)
            {
                string str = string.Format("[{0}]: ", dt);
                Console.Write(str);
                WriteToLogFile(str);
                _newLine = false;
            }
            Console.ForegroundColor = ConsoleColor.Green;
            Console.Write(msg);
            WriteToLogFile(msg);
            Console.ForegroundColor = color;
        }

        public static void PrintLn(string format, params object[] args)
        {
            Print(format, args);
            WriteToLogFile("\r\n");
            Console.WriteLine();
            _newLine = true;
        }

        public static void Error(string format, params object[] args)
        {
            string msg = GetMessage(format, args);
            DateTime dt = System.DateTime.Now.ToLocalTime();

            ConsoleColor color = Console.ForegroundColor;
            Console.ForegroundColor = ConsoleColor.Red;
            if (!_newLine)
            {
                WriteToLogFile("\r\n");
                Console.WriteLine();
            }
            string str = string.Format("[{0}]: {1}", dt, msg);
            WriteToLogFile(str + "\r\n");
            Console.WriteLine(str);
            Console.ForegroundColor = color;
            _newLine = true;
        }

        private static string GetMessage(string format, object[] args)
        {
            if (args.Length > 0)
            {
                return string.Format(format, args);
            }
            else
            {
                return format;
            }
        }

        private static void WriteToLogFile(string str)
        {
            if (!WriteToFile)
            {
                return;
            }
            if (string.IsNullOrEmpty(str))
            {
                return;
            }

            str = str.Replace("\b", "");
            StreamWriter sw = new StreamWriter(c_LogFile, true);
            sw.Write(str);
            sw.Flush();
            sw.Close();
        }

        public const string c_LogFile = "log.txt";
        private static bool _newLine = true;
        public static bool WriteToFile = false;
    }



    ///////////////////////////////////////////////////////////////////////////////////////////////////////////




    ///////////////////////////////////////////////////////////////////////////////////////////////////////////




    ///////////////////////////////////////////////////////////////////////////////////////////////////////////
    public class WebRobot
    {
        //==============================Private=======================================
        private Boolean killMe = true;
        private Thread robotThread = null;
        private HttpHelper _http;

        private bool initNetwork()
        {
            this._http = new HttpHelper();
            try
            {
                string dir = Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName);
                string filename = dir + "\\Config.txt";

                if (!File.Exists(filename))
                {
                    Console.WriteLine("缺少{0}文件，系统加载默认配置.", filename);
                    return true;
                }

                this._http.SetConfig(GlobalConfigHelper.ReadGlobalConfigData(filename));
            }
            catch (Exception e)
            {
                TraceLog.Error(e.Message);
                return false;
            }
            return true;
        }


        //==============================Proctected=====================================
        protected string userName;
        protected string passWord;
        

        protected bool needStop() { return (this.killMe); }
        protected virtual void ThreadAction() {  }
        protected string Get(string url) { return this._http.Get(url); }
        protected string Post(string url, string data) { return this._http.Post(url, data); }


        //==============================Public========================================
        public void Start()
        {
            if ( !this.initNetwork() ) return;
            this.killMe = false;
            this.robotThread = new Thread(new ThreadStart(this.ThreadAction));
            this.robotThread.Start();
        }

        public void Stop()
        {
            this.killMe = true;
            //Thread.Sleep(3*1000); 
            if (this.robotThread != null) this.robotThread.Join();
        }

    }


    ///////////////////////////////////////////////////////////////////////////////////////////////////////////
    public class TestRobot : WebRobot
    {
        protected string realName = null;
        protected string userIdx = null;

        protected override void ThreadAction()
        {
            if ( ! this.needStop() )
            {
                this.logon();
            }
        }

        public TestRobot(string un, string pw)
        {
            this.userName = un;
            this.passWord = pw;
            this.Start();
        }

        private bool logon( )
        {
            if ((String.IsNullOrEmpty(this.userName)) || (String.IsNullOrEmpty(this.passWord)))
            {
                return false;
            }
            TraceLog.Print("Login as [{0}]: ", this.userName);
            TextParser tp = new TextParser(this.Post("http://www.kaixin001.com/login/login.php", "url=%2F&email=" + HttpHelper.EncodePostData(this.userName) + "&password=" + HttpHelper.EncodePostData(this.passWord)));

            string strId;

            if ( (strId = tp.getStrBtw("<b class=\"f14\">", "</b>")) != null )
            {
                this.realName = strId;
            }
            else
            {
                this.realName = "";
            }


            if (tp.contains("<title>我的首页 - 开心网</title>"))
            {
                this.userIdx = tp.getStrBtw("我的开心网ID:", "\"");
                TraceLog.PrintLn("Ok!");
                TraceLog.PrintLn("Id={0}, Name={1}", this.userIdx, this.userName);
                return true;
            }
            else
            {
                TraceLog.Error("Failed!");
                return false;
            }
        }
    }

}
